package com.springboot.frame;

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.retry.*;
import org.springframework.retry.annotation.Backoff;
import org.springframework.retry.annotation.EnableRetry;
import org.springframework.retry.annotation.Recover;
import org.springframework.retry.annotation.Retryable;
import org.springframework.retry.support.DefaultRetryState;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;
import org.springframework.util.ObjectUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.Arrays;

/**
 * 启动类
 * <p>
 * 参考：https://springboot.io/t/topic/2735
 */
@EnableRetry //重试注解
@SpringBootApplication
public class SpringBootRetryApplication {

    public static void main(String[] args) {
        SpringApplication.run(SpringBootRetryApplication.class, args);
    }

    @RestController
    @RequestMapping("/")
    class IndexController {
        @Autowired
        private RetryService retryService;

        @GetMapping("/create")
        public String index() throws Exception {
            return retryService.retryTransferAccounts();
        }
    }

    @Service
    class RetryService {

        private int count = 0;

        /**
         * @return
         * @throws Exception
         * @Retryable 参数    含义
         * interceptor	重试拦截器bean名称，用于可重试方法
         * value	可重试的异常类型。含义同include。
         * 默认为空(如果excludes也为空，则重试所有异常)
         * include	可重试的异常类型。默认为空(如果excludes也为空，则重试所有异常)
         * exclude	无需重试的异常类型。默认为空(如果includes也为空，则重试所有异常)
         * label	统计报告的唯一标签。如果未提供，
         * 则调用者可以选择忽略它或提供一个默认值。
         * 我的理解就是这个重试方法的唯一名称
         * stateful	若为true，标志重试是有状态的：即重新抛出异常，
         * 但是重试策略与相同的策略应用于具有相同参数的后续调用。
         * 若为false，则不会重新引发可重试的异常
         * maxAttempts	最大重试次数(包括第一次失败)，默认为3次
         * maxAttemptsExpression	计算最大尝试次数(包括第一次失败)的表达式，默认为3 次
         * backoff	重试等待策略,下面会在@Backoff中介绍
         * exceptionExpression	指定在SimpleRetryPolicy.canRetry()返回true之后要求值的表达式-可用于有条件地禁止重试。
         * @Backoff delay    重试之间的等待时间(以毫秒为单位)
         * maxDelay	重试之间的最大等待时间(以毫秒为单位)
         * multiplier	指定延迟的倍数
         * delayExpression	重试之间的等待时间表达式
         * maxDelayExpression	重试之间的最大等待时间表达式
         * multiplierExpression	指定延迟的倍数表达式
         * random	随机指定延迟时间
         */

        @Retryable(value = Exception.class,
                maxAttempts = 3,
                listeners = {"defaultRetryListener"},
                //interceptor = "retryInterceptor",
                backoff = @Backoff(
                        delay = 1000,
                        multiplier = 1,
                        maxDelay = 3000))
        public String retryTransferAccounts() throws Exception {
            count++;
            int a = 2 / 0;
            throw new Exception();
        }


        @Recover
        public String callback(Exception e) {
            System.out.println("回调次数用尽，回调方法执行！！！执行了 " + this.count + " 次");
            return ("回调次数用尽，回调方法执行！！！执行了 " + this.count + " 次");
        }
    }

    @Component("defaultRetryListener")
    class DefaultRetryListener implements RetryListener {

        @Override
        public <T, E extends Throwable> boolean open(RetryContext context, RetryCallback<T, E> callback) {
            System.out.println("==前置回调==");
            return true;
        }

        @Override
        public <T, E extends Throwable> void close(RetryContext context, RetryCallback<T, E> callback, Throwable throwable) {
            System.out.println("==后置回调==");
        }

        @Override
        public <T, E extends Throwable> void onError(RetryContext context, RetryCallback<T, E> callback, Throwable throwable) {
            System.out.println("==执行报错==");
        }
    }

    @Component("retryInterceptor")
    class RetryInterceptor implements MethodInterceptor {

        @Override
        public Object invoke(final MethodInvocation invocation) throws Throwable {
            return ">>>>>>>>>>>>>>>";
        }
    }
}
